#include "applyMsg.h"
#include "persister.h"
#include "raftStub.h"
#include "ratfRPC.pb.h"
#include "util.h"
#include <boost/serialization/serialization.hpp>
#include <chrono>
#include <memory>
#include <mutex>
#include <vector>


namespace raftStoreDataBase
{
    // 为了方便调试，设置标志rpc的状态
    constexpr int Disconnected = 0;
    constexpr int AppNormal = 1;

    constexpr int killed = 0;
    constexpr int Voted = 1;  // 编译时常量
    constexpr int Expire = 2;
    constexpr int Normal = 3;

    class Raft : public raftRpcProtoc::raftRpc
    {
    public:
        Raft();
        ~Raft();
        void init(std::vector<std::shared_ptr<raftStub>> peers,int id,std::shared_ptr<Persister> persister,std::shared_ptr<LockQueue<ApplyMsg>> applych);

        // 这是重写的raftRPC类的方法，
        /**     ratfRPC.proto里面定义了我们的rpc服务的
         * @brief  service的调用过程是：
                service->callmethod()---> raftRPC的callmethod()-->根据传递的参数去调用相应的方法（假设是AppendEntries请求）
                调用的是raftRPC类的AppendEntries方法-----> 我们的自定义类继承了raftRPC重写了可调用方法
                因此----->调用的是我们重写的方法
         *
         * @param controller
         * @param request
         * @param response
         * @param done
         */
        void start(Op command,int *newLogIndex,int *newLogTerm,bool *isLeader);
        void AppendEntries( google::protobuf::RpcController *controller,
                            const raftRpcProtoc::AppendEntriesArgs *request,
                            raftRpcProtoc::AppendEntriesReply *response,
                            google::protobuf::Closure *done ) override;

        void InstallSnapshot( google::protobuf::RpcController *controller,
                              const raftRpcProtoc::InstallSnapshotRequest *request,
                              raftRpcProtoc::InstallSnapshotResponse *response,
                              google::protobuf::Closure *done ) override;
        void RequestVote( google::protobuf::RpcController *controller,
                          const raftRpcProtoc::RequestVoteArgs *request,
                          raftRpcProtoc::RequestVoteReply *response,
                          google::protobuf::Closure *done ) override;

        // 持久化存储函数
        void persist();

        // 查找最后最后一个日志索引,如果不存在的话，就是从快照去查找呀
        int getLastLogIndex();
        // 成员属性

        /**
         * @brief
         *
         * @param index 这
         * @param term
         * @return true
         * @return false
         */
        bool mathcLog( int index, int term );  // 这个类是用来验证发送过来的日志的index和term是否和我存储的日志条目的index和term匹配
        // 进来之前需要判断index是否存在的，即大于等于我快照最后的一个index和大于等于我之前保存的日志的index；如果在这个区间内说明你是存在于我的日志条目中的index；

        int getSlicesIndexFromLogIndex( int logIndex );

        /**
         * @brief Get the Log Term From Log Index object
         *
         * @param logIndex 日志条目的索引
         * @return int  返回值是返回该日志条目的任期term
         */
        int getLogTermFromLogIndex( int logIndex );

        int getLastLogTerm();
        std::vector<ApplyMsg> getApplyLogs();

        void getState(int *term,bool *isLeader);

        void makeSnapshot(int index,std::string snapshot);
    private:
        void AppendEntriesImp( const raftRpcProtoc::AppendEntriesArgs *request, raftRpcProtoc::AppendEntriesReply *response );
        void InstallSnapshotImp( const raftRpcProtoc::InstallSnapshotRequest *request, raftRpcProtoc::InstallSnapshotResponse *response );
        void RequestVoteImp( const raftRpcProtoc::RequestVoteArgs *request, raftRpcProtoc::RequestVoteReply *response );
        void getLastLogIndexAndTerm( int *lastLogIndex, int *lastLogTerm );
        bool compareLogIndexAndTerm( int index, int term );

        // 开始选举
        void doElection();

        // 发送requestVote的例子
        bool sendRequestVote( int peer,
                              std::shared_ptr< raftRpcProtoc::RequestVoteArgs > request,
                              std::shared_ptr< raftRpcProtoc::RequestVoteReply > reply,
                              std::shared_ptr< int > votedNum );
        void doHeartBeat();

        void leaderSendSnapShot( int peer );

        void getPreLogIndexAndTerm( int peer, int *index, int *term );

        void leaderUpdateCommintIndex();

        bool sendAppendEntries( int peer,
                                std::shared_ptr< raftRpcProtoc::AppendEntriesArgs > request,
                                std::shared_ptr< raftRpcProtoc::AppendEntriesReply > reply,
                                int &appendNum );
        void pushMsgToKvServer( const ApplyMsg &appMsg )const;
        std::string persistData();

        // 该方法是提交日志到状态机中
        void applierTicker();

        // 定时需要检查选举超时没
        void electionTimeOutTicker();

        void leaderHearBeatTicker();

        int getNewCommandIndex();

        int getRaftStateSize(){return m_persister->RaftStateSize();}

        void readPersist(std::string data);



    private:
        std::mutex mutex_;
        // 还需要保存与其它raft节点通信的RPC入口,就是通过代理Stub来调用别的节点上面的方法
        // 客户端调用远程服务端的代理，当发起连接时就会保存到这里
        std::vector< std::shared_ptr< raftStub > > m_peers;

        // 持久化层
        std::shared_ptr< Persister > m_persister;

        // 定义当前的节点id和当前投票给谁了，和自己当前的任期
        int m_id;
        int m_currentTerm;
        int m_vectorFor;

        // 日志条组
        std::vector< raftRpcProtoc::LogEntry >
            m_logs;  // 日志条目数组，包含了状态机要执行的指令集，以及收到领导时的任期号，这里面会存放LogEntry日志条目
        // 通过获取容器最后一个的元素中的index和term就能获取到logIndex和logTerm，当这个容器的数据很大了之后，需要将其持久化存储并且清空这个容器

        int m_commitIndex;  // 这是当前需要提交的日志的最后一个日志的索引处，
        int m_lastApplied;  // 经汇报给状态机（上层应用）的log 的index
        // 这两个变量就控制着需要上传到上层应用的日志条目，m_commitIndex需要提交的最后一个日志的索引，m_lastApplied上一次提交完成的最后一个索引，因此
        // 需要提交的日志索引的范围是，从m_lastApplied-->m_commitIndex

        std::vector< int > m_nextIndex;  // 这是当日志不匹配时需要发送的下一个索引，这是由服务器来维护的
        // 还需要记录匹配的index
        std::vector< int > m_matchIndex;  // m_matchIndex[i] 代表 跟随者节点 i 的日志中，已经复制到的最高日志索引。

        // 同时需要标识我的身份
        enum role
        {
            Follower,
            Candidate,
            Leader
        };

        role m_role;

        // 需要一个容器存放从服务器获得数据，从而交给上层应用
        std::shared_ptr< LockQueue< ApplyMsg > > applyChan;  // client从这里取日志（2B），client与raft通信的接口

        // 选举超时时间
        std::chrono::_V2::system_clock::time_point m_lastResetElectionTime;
        // 心跳超时
        std::chrono::_V2::system_clock::time_point m_lastHeartBeatTime;

        // 储存了快照中的最后一个日志的Index和Term
        int m_lastSnapShotIndex;
        int m_lastSnapShotTerm;

        // 协程，先不写

        std::mutex m_mutex;

    private:
        //序列化和反序列化类
        class BoostPersistRaftNode
        {
        public:
            friend class boost::serialization::access;  //通过设置友元函数让access类能够访问该类的私有成员
            //序列化模板类，通过该模板，boost可以根据此方法进行序列化操作，这是关键的操作
            template<class Archive>
            void serialize(Archive &ar,const unsigned char version)
            {
                ar &m_currentTerm;
                ar &m_vectorFor;
                ar &m_lastSnapshotIndex;
                ar &m_lastSnapShotTerm;
                ar &m_logs;
            }

        public:
            int m_currentTerm;
            int m_vectorFor;
            int m_lastSnapshotIndex;
            int m_lastSnapShotTerm;
            std::vector<std::string> m_logs;
            std::unordered_map<std::string,int> map;
        };
    };
}  // namespace raftStoreDataBase